package com.yystar.support.filter;

import java.io.File;
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.yystar.support.servlet.HeaderCacheServletResponse;
import com.yystar.support.util.CacheUtility;
import com.yystar.support.util.FileUtility;
import com.yystar.support.util.MinifierUtility;
import com.yystar.support.util.SystemProperties;
import com.yystar.support.util.WTextUtility;

/**
 * 压缩过滤器<br>
 * 当前只过滤了JS和CSS请求。
 * @author liuzhifei
 *
 */
public class MinifierFilter implements Filter {
    
    private ServletContext _servletContext;

    private static final String _CSS_EXTENSION = ".css";
    
    private static final String _JAVASCRIPT_EXTENSION = ".js";
    
    //压缩后缓存的目录
    private static final String _TEMP_DIR = System.getProperty("catalina.base") + "/temp/sb2-bp/minifier";
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        
        _servletContext = filterConfig.getServletContext();
        
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
        
        HttpServletRequest request = (HttpServletRequest) req;
        HeaderCacheServletResponse headerCacheServletResponse = 
                (HeaderCacheServletResponse) resp;
        
        //如果项目不是开发模式 则进行压缩
        if(!WTextUtility.parseBooleanString(
                SystemProperties.get("is.development.model"))){
        
            //获取压缩后的数据
            String minifiedContent = getMinifiedContent(
                    request, headerCacheServletResponse, filterChain);
    
            //如果压缩后的数据不为null 则输出到压缩后的数据
            if(minifiedContent != null){
                
                ServletOutputStream outputStream = headerCacheServletResponse.getOutputStream();
                
                outputStream.write(minifiedContent.getBytes());
                
                outputStream.close();
                
                return;
            }
        }
        
        filterChain.doFilter(request, headerCacheServletResponse);
    }

    /**
     * 获取压缩后的数据<br>
     * 如果URL带有参数 source=js 或  source=css 则不进行压缩
     * @param request
     * @param response
     * @param filterChain.
     * @return 压缩后的数据, 不成功压缩则返回null
     * @throws IOException
     */
    protected String getMinifiedContent(HttpServletRequest request, HeaderCacheServletResponse response, FilterChain filterChain) throws IOException {

        String source = WTextUtility.parseNullString(request.getParameter("source"));

        //参数 source 不是为空的都不进行压缩
        if(WTextUtility.isNotNull(source)){
            return null;
        }
        
        String requestPath = request.getRequestURI();
        
        String suffix = requestPath.substring(requestPath.lastIndexOf('.'));
        
        if ((!WTextUtility.parseBooleanString(
                        SystemProperties.get("js.fast.load")) && suffix.equalsIgnoreCase(_JAVASCRIPT_EXTENSION))
                || (!WTextUtility.parseBooleanString(
                        SystemProperties.get("css.fast.load")) && suffix.equalsIgnoreCase(_CSS_EXTENSION))) {

            return null;
        }
        
        CacheResponseData data = CacheUtility.getCacheData(requestPath);
        
        //如果存在缓存 则使用缓存数据
        if(data != null && data.getContent() != null){
            response.setContentType(data.getContentType());
            return new String(data.getContent());
        }

        String contextPath = request.getContextPath();

        if (!contextPath.equals("/")) {
            requestPath = requestPath.substring(contextPath.length());
        }

        String realPath = _servletContext.getRealPath(requestPath);

        if (realPath == null) {
            return null;
        }

        realPath = realPath.replace('\\', '/');

        File file = new File(realPath);

        if (!file.exists()) {
            return null;
        }

        //生成缓存路径路径+缓存文件名
        String cacheCommonFileName = getCacheFileName(request);
        
        if(WTextUtility.isNull(cacheCommonFileName)){
            return null;
        }

        File cacheContentTypeFile = new File(
            cacheCommonFileName + "_CONTENT_TYPE");
        File cacheDataFile = new File(cacheCommonFileName + "_DATA");
        String minifiedContent = null;
        
        //如果缓存文件存在， 并且缓存文件的最后修改的时间 大于或等于 请求文件的最后修改时间则该缓存可以继续使用
        if ((cacheDataFile.exists()) &&
            (cacheDataFile.lastModified() >= file.lastModified())) {

            //如果该缓存文件的contentType缓存文件存在，也将读取出来并设置到响应头
            if (cacheContentTypeFile.exists()) {
                String contentType = FileUtility.read(cacheContentTypeFile);

                response.setContentType(contentType);
            }
            
            //从缓存文件里读取压缩后的缓存内容
            minifiedContent =  FileUtility.read(cacheDataFile);
            
            //服务端同步缓存
            minifiedCache(request, response, minifiedContent, cacheDataFile.lastModified());

            return minifiedContent;
        }

        //不同的后缀文件请求处理不同的压缩，当前只处理CSS和JS压缩
        if (_CSS_EXTENSION.equalsIgnoreCase(suffix)) {

            minifiedContent = minifyCss(file);

            response.setContentType("text/css");

            FileUtility.write(cacheContentTypeFile, "text/css");
        }
        else if (_JAVASCRIPT_EXTENSION.equalsIgnoreCase(suffix)) {

            minifiedContent = minifyJavaScript(file);

            response.setContentType("text/javascript");

            FileUtility.write(cacheContentTypeFile, "text/javascript");
        }
        else{
            return null;
        }
        
        //将压缩内容写入到缓存文件里
        FileUtility.write(cacheDataFile, minifiedContent);
        
        //服务端同步缓存
        minifiedCache(request, response, minifiedContent, cacheDataFile.lastModified());
        
        return minifiedContent;
    }
    
    /**
     * 将压缩后的内容加入到服务端缓存
     * @param request 
     * @param response
     * @param minifiedContent 压缩后的内容
     * @param lastModified 最后缓存的时间
     */
    protected void minifiedCache(HttpServletRequest request, HeaderCacheServletResponse response, String minifiedContent, long lastModified){
        
        if(WTextUtility.isNotNull(minifiedContent)){

            String cacheControl = WTextUtility.parseNullString(response.getHeader("Cache-Control"));
            
            //如果响应头没有设置了 no-cache 并且响应状态是 200 的请求下进行缓存
            if(!cacheControl.contains("no-cache")
                    && response.getStatus() == HttpServletResponse.SC_OK){
                
                String uri = request.getRequestURI();
                
                byte[] data = minifiedContent.getBytes();
                
                CacheResponseData cacheResponseData = CacheUtility.getCacheData(uri);
                
                //如果存在缓存 则重新修改下缓存数据
                if(cacheResponseData == null){
                    
                    cacheResponseData = new CacheResponseData(data, lastModified);
                    cacheResponseData.setContentType(response.getContentType());
                    
                    CacheUtility.putCacheData(uri, cacheResponseData);
                }
                else{
                    
                    cacheResponseData.setContent(data);
                    cacheResponseData.setLastModified(lastModified);
                    cacheResponseData.setContentType(response.getContentType());
                }
            }
        }
    }
    
    /**
     * 根据文件压缩CSS
     * @param file 要压缩的CSS文件
     * @return 压缩后的内容
     * @throws IOException
     */
    protected String minifyCss(File file) throws IOException {
        
        String content = FileUtility.read(file);
        
        return minifyCss(content);
    }

    /**
     * 根据内容压缩CSS
     * @param content 要压缩的CSS内容
     * @return 压缩后的内容
     */
    protected String minifyCss(String content) {

        return MinifierUtility.minifyCss(content);
    }

    /**
     * 根据文件压缩javaScript
     * @param file 要压缩的JS文件
     * @return 压缩后的内容
     * @throws IOException
     */
    protected String minifyJavaScript(File file) throws IOException {
        
        String content = FileUtility.read(file);

        return minifyJavaScript(content);
    }

    /**
     * 根据内容压缩javaScript
     * @param content 要压缩的JS内容
     * @return 压缩后的内容
     */
    protected String minifyJavaScript(String content) {

        return MinifierUtility.minifyJavaScript(content);
    }

    /**
     * 生成本地缓存文件名路径
     * @param request 当前的请求
     * @return 缓存路径+缓存文件名
     */
    protected String getCacheFileName(HttpServletRequest request){
         
         return _TEMP_DIR.concat("/").concat(CacheUtility.getCacheKey(request.getRequestURI()));
    }

    @Override
    public void destroy() {

    }

}
